#!/usr/bin/env python

import threading
import os
import sys
import StringIO

from third_party.fusepy import fuse

class PatchFs(fuse.LoggingMixIn, fuse.Operations):    
  def __init__(self, base_root, workspace_root):
    self._base_root = os.path.realpath(base_root)
    self._workspace_root = os.path.relpath(workspace_root)

    self._workspace_lock = threading.Lock()

    # We don't need a lock for the base since we only read from it.

  def _GetPathFromRoot(self, root, path):
    while path.startswith('/'):
      path = path[1:]
    return os.path.join(root, path)

  def _GetBasePath(self, path):
    return self._GetPathFromRoot(self._base_root, path)

  def _GetWorkspacePath(self, path):
    return self._GetPathFromRoot(self._workspace_root, path)
  
  def readdir(self, path, fh):
    base_path = self._GetBasePath(path)
    return ['.', '..'] + os.listdir(base_path)

  def getattr(self, path, fh=None):

    # TODO: check workspace first.

    base_path = self._GetBasePath(path)
    st = os.lstat(base_path)
    return dict((key, getattr(st, key)) for key in (
        'st_atime', 'st_ctime',
        'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))

  def read(self, path, size, offset, fh):
    workspace_path = self._GetBasePath(path)

    data = self._ReadPath(path)
    return data[offset : (size + offset)]
          
  def _ReadPath(self, path):
    workspace_path = self._GetBasePath(path)
    if os.path.isfile(workspace_path):
      with self._workspace_lock:
        open(workspace_path, 'r').read()

    base_path = self._GetBasePath(path)
    return open(base_path, 'r').read()

  def write(self, path, data, offset, fh):

    workspace_data = self._ReadPath(path)
    buffer = StringIO.StringIO(workspace_data)
    buffer.seek(offset)
    buffer.write(data)
    buffer.seek(0)

    buf_data = buffer.getvalue()
    self._WritePath(path, buffer.read())

    return len(data)

  def truncate(self, path, length, fh=None):
    data = self._ReadPath(path)
    data = data[0:length]
    self._WritePath(path, data)

  def _WritePath(self, path, data):
    workspace_path = self._GetWorkspacePath(path)

    dirpath, filename = os.path.split(workspace_path)

    try:
      os.makedirs(dirpath)      
    except OSError:
      pass

    with self._workspace_lock:
      with open(workspace_path, 'w') as f:
        f.write(data)


if __name__ == "__main__":
  argv = sys.argv

  if len(argv) != 4:
    print 'usage: %s <root> <workspace> <mountpoint>' % argv[0]
    sys.exit(1)

  base_root, workspace_root, mountpoint = argv[1:]

  f = fuse.FUSE(PatchFs(base_root, workspace_root), mountpoint, foreground=True)
